<template>
  <div
    ref="block"
    :class="`column block ${handle ? 'bordered' : ''} ${
      selected ? 'selected' : ''
    }`"
    :style="'width: ' + width + 'px;'"
  >
    <div
      class="upper col-8"
      :style="`background-color:${handle ? bgColor : 'transparent'}`"
      @mousedown="startDrag($event, 'middle')"
    >
      <canvas ref="canvas" />
    </div>
    <div class="lower row justify-between">
      <div
        v-if="width > 50"
        class="name-label text-no-wrap one-line-text non-selectable"
      >
        {{ name }}
      </div>
      <q-btn
        v-if="width > 50 && handle"
        padding="none"
        size="xs"
        flat
        icon="mdi-dots-horizontal"
      >
        <q-menu>
          <q-list dense>
            <q-item clickable>
              <q-item-section>Front Color</q-item-section>
              <q-item-section side>
                <q-icon name="keyboard_arrow_right" />
              </q-item-section>
              <q-menu anchor="top end" self="top start">
                <q-color
                  v-model="waveformColor"
                  no-header
                  no-footer
                  class="my-picker"
                />
              </q-menu>
            </q-item>
            <q-item clickable>
              <q-item-section>Background Color</q-item-section>
              <q-item-section side>
                <q-icon name="keyboard_arrow_right" />
              </q-item-section>
              <q-menu anchor="top end" self="top start">
                <q-color
                  v-model="bgColor"
                  no-header
                  no-footer
                  class="my-picker"
                />
              </q-menu>
            </q-item>
            <q-item clickable @click="emit('rename')">
              <q-item-section>Rename</q-item-section>
            </q-item>
            <q-item clickable @click="emit('delete')" v-close-popup>
              <q-item-section>Delete</q-item-section>
            </q-item>
          </q-list>
        </q-menu>
      </q-btn>
    </div>
    <div
      class="handle left"
      v-if="handle"
      @mousedown="startDrag($event, 'left')"
    ></div>
    <div
      class="handle right"
      v-if="handle"
      @mousedown="startDrag($event, 'right')"
    ></div>
  </div>
</template>
<script setup lang="ts">
import { ref, watch, onMounted } from 'vue';
import drawWaveform from 'src/utils/WaveformUtils';
import AudioBlockInfo from './AudioBlockInfo';
import useUndoRedo from 'src/mixins/UndoRedo';
import StudioOperate, { OperateType } from 'src/models/StudioOperate';
import { throttle } from 'quasar';
const props = defineProps({
  scale: {
    type: Number,
    default: 0,
  },
  handle: {
    type: Boolean,
    default: false,
  },
  zoom: {
    type: Number,
    default: 1,
  },
  audioBuffer: {
    type: AudioBuffer,
    required: true,
  },
  name: {
    type: String,
    default: '',
  },
  start: {
    type: Number,
    default: 0,
  },
  blob: {
    type: Blob,
  },
  info: {
    type: Object as any,
    default: undefined,
  },
});

const emit = defineEmits(['delete', 'update', 'rename']);
const { addAction } = useUndoRedo();
const start = ref(props.start);
const duration = ref(0);
const paddingLeft = ref(0);
const paddingRight = ref(0);
const block = ref<HTMLDivElement>();
const waveformColor = ref('#fff');
const bgColor = ref('rgba(149,165,166,0.5)');
const canvas = ref<HTMLCanvasElement>();
const width = ref(0);
const selected = ref(false);
let originalData: Float32Array = new Float32Array(0);
function select() {
  selected.value = true;
}
function unselect() {
  selected.value = false;
}
watch(start, () => {
  fixedRefresh();
});
watch(
  () => props.scale,
  () => {
    fixedRefresh();
  }
);
watch(
  () => props.zoom,
  () => {
    fixedRefresh();
  }
);
watch(duration, () => {
  fixedRefresh();
});
watch(waveformColor, (color) => {
  updateWaveform();
});
const fixedRefresh = throttle(refresh, 100);
function refresh() {
  const x = props.scale * start.value * props.zoom;
  width.value = props.scale * duration.value * props.zoom;
  if (props.name !== 'Video') {
    console.log(width.value);
  }

  if (canvas.value && block.value) {
    canvas.value.width = width.value;
    canvas.value.height = 55;
    block.value.style.left = `${x}px`;
    // block.value!.style.width = `${width.value}px`;
    updateWaveform();
  }
}
const fixedUpdateWaveform = throttle(updateWaveform, 500);
function updateWaveform() {
  let startIndex = Math.floor(paddingLeft.value * props.audioBuffer.sampleRate);
  if (startIndex < 0) startIndex = 0;
  let endIndex = Math.floor(
    (props.audioBuffer.duration - paddingRight.value) *
      props.audioBuffer.sampleRate
  );
  const result = originalData.slice(startIndex, endIndex);
  if (canvas.value) {
    drawWaveform(canvas.value, result, {
      color: waveformColor.value,
      barWidth: 1,
      gapWidth: 1,
      maxVal: 0.4,
      linePercent: 0.5,
    });
  }
}
let pos = { x: 0, left: 0, width: 0 };
let side = '';
let oldStart = 0;
const isDragging = ref(false);
let oldInfo: any;
function startDrag(event: MouseEvent, dir: 'left' | 'right' | 'middle') {
  if (!props.handle) {
    return;
  }
  isDragging.value = true;
  oldInfo = getInfo();
  window.addEventListener('mouseup', onMouseUp);
  window.addEventListener('mousemove', onMouseMove);
  side = dir;
  pos.x = event.clientX;
  if (side === 'left') {
    // slice left
    pos.left = parseInt(block.value!.style.left) | 0;
    pos.width = block.value!.clientWidth;
    oldStart = start.value;
  } else if (side === 'right') {
    // slice right
    pos.width = block.value!.clientWidth;
  } else {
    // change position
    pos.left = parseInt(block.value!.style.left) | 0;
  }
}
function onMouseUp(e: MouseEvent) {
  e.preventDefault();
  e.stopPropagation();
  isDragging.value = false;
  window.removeEventListener('mouseup', onMouseUp);
  window.removeEventListener('mousemove', onMouseMove);
  emit('update', getCurrentInfo());
  pos.x = 0;
  const operate = new StudioOperate();
  operate.type = OperateType.ChangeBlock;
  operate.action = 'changeBlock';
  operate.undo = setInfo;
  operate.undoParams = [oldInfo];
  operate.redo = setInfo;
  operate.redoParams = [getInfo()];
  addAction(operate);
}
function onMouseMove(event: MouseEvent) {
  event.preventDefault();
  event.stopPropagation();
  if (side === 'left') {
    pos.left += event.movementX;
    pos.width -= event.movementX;
    if (px2time(pos.width) <= props.audioBuffer.duration) {
      start.value = px2time(pos.left);
      paddingLeft.value = start.value - oldStart;
      if (paddingLeft.value < 0) paddingLeft.value = 0;
      block.value!.style.width = `${pos.width}px`;
      duration.value = px2time(pos.width);
    }
    fixedUpdateWaveform();
  } else if (side === 'right') {
    pos.width += event.movementX;
    if (pos.width < 6) {
      pos.width = 6;
    }
    if (px2time(pos.width) <= props.audioBuffer.duration) {
      block.value!.style.width = `${pos.width}px`;
      canvas.value!.width = pos.width;
      duration.value = px2time(pos.width);
      paddingRight.value = props.audioBuffer.duration - duration.value;
      fixedUpdateWaveform();
    }
  } else {
    pos.left += event.movementX;
    if (pos.left < 0) {
      pos.left = 0;
    }
    block.value!.style.left = `${pos.left}px`;
    start.value = px2time(pos.left);
  }
}

onMounted(() => {
  start.value = props.start;
  duration.value = props.audioBuffer.duration;
  originalData = props.audioBuffer.getChannelData(0);
  if (props.info) {
    setInfo(props.info);
  } else {
    refresh();
  }
});
function px2time(px: number) {
  return px / (props.scale * props.zoom);
}
function getCurrentInfo() {
  const info = new AudioBlockInfo();
  info.name = props.name;
  info.audioBuffer = props.audioBuffer;
  info.blob = props.blob;
  info.start = start.value;
  info.duration = duration.value;
  info.paddingLeft = paddingLeft.value;
  info.paddingRight = paddingRight.value;
  info.color = waveformColor.value;
  info.bgColor = bgColor.value;
  return info;
}
function getInfo() {
  return {
    start: start.value,
    name: props.name,
    duration: duration.value,
    paddingLeft: paddingLeft.value | 0,
    paddingRight: paddingRight.value | 0,
    color: waveformColor.value,
    bgColor: bgColor.value,
  };
}
function setInfo(info: any) {
  start.value = info.start + info.paddingLeft;
  duration.value =
    info.audioBuffer.duration - info.paddingRight - info.paddingLeft;
  paddingLeft.value = info.paddingLeft;

  paddingRight.value = info.paddingRight;
  waveformColor.value = info.color!;
  bgColor.value = info.bgColor!;
  refresh();
}
defineExpose({ getCurrentInfo, refresh, setInfo, select, unselect });
</script>
<style lang="sass" scoped>

$border-color: #95A5A6
.bordered
  border: 1px solid $border-color
  border-radius: 2px
.selected
  border: 1px solid #1ABC9C
.block
  position: absolute
  overflow: hidden
  .upper
    height: 55px
    cursor: grab
  .lower
    height: 22px
    background-color: rgba(43,47,47)
    .name-label
      font-size: 12px
      min-width: 1em
      width: 30%
  .handle
    width: 5px
    height: 25px
    background-color: $border-color
    position: absolute
  .left
    left: 0px
    bottom: 25px
    cursor: e-resize
  .right
    right: 0px
    bottom: 25px
    cursor: e-resize
</style>
